﻿// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved

#include "stdafx.h"

const size_t c_bufferSize = sizeof(FILE_NOTIFY_INFORMATION) * 1000;

// Run of the mill directory watcher to signal when user causes things to
// happen in the client folder sync root


void DirectoryWatcher::Initalize(
    _In_ PCWSTR path, 
    _In_ std::function<void(std::list<WatchObject>&)> callback)
   
{
    _path = path;
    _notify.reset(reinterpret_cast<FILE_NOTIFY_INFORMATION*>(new char[c_bufferSize]));

    _callback = callback;
    
    _dir.attach(CreateFile(path,
        FILE_LIST_DIRECTORY,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        nullptr,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
        nullptr));
    if (_dir.get() == INVALID_HANDLE_VALUE)
    {
        throw winrt::hresult_error(HRESULT_FROM_WIN32(GetLastError()));
    }
}

concurrency::task<void> DirectoryWatcher::ReadChangesAsync()
{
    auto token = _cancellationTokenSource.get_token();
    return concurrency::create_task([this, token]
    {
        while (true)
        {
            /*
#define FILE_NOTIFY_CHANGE_FILE_NAME    0x00000001
#define FILE_NOTIFY_CHANGE_DIR_NAME     0x00000002
#define FILE_NOTIFY_CHANGE_ATTRIBUTES   0x00000004
#define FILE_NOTIFY_CHANGE_SIZE         0x00000008
#define FILE_NOTIFY_CHANGE_LAST_WRITE   0x00000010
#define FILE_NOTIFY_CHANGE_LAST_ACCESS  0x00000020
#define FILE_NOTIFY_CHANGE_CREATION     0x00000040
#define FILE_NOTIFY_CHANGE_SECURITY     0x00000100

            */
  
            DWORD returned;
            winrt::check_bool(ReadDirectoryChangesW(
                _dir.get(),
                _notify.get(),
                c_bufferSize,
                    TRUE,
                    FILE_NOTIFY_CHANGE_FILE_NAME
                    | FILE_NOTIFY_CHANGE_DIR_NAME
                 //   | FILE_NOTIFY_CHANGE_LAST_WRITE
                    | FILE_NOTIFY_CHANGE_ATTRIBUTES,  //脱水，注水
                &returned,
                &_overlapped,
                nullptr));
#if 0
            DWORD transferred;
            if (GetOverlappedResultEx(_dir.get(), &_overlapped, &transferred, 1000/*INFINITE*/, FALSE))
#endif
            while (true)
            {
                DWORD transferred;
                if (GetOverlappedResult(_dir.get(), &_overlapped, &transferred, FALSE))
                {
                    std::list<WatchObject> result;
                    FILE_NOTIFY_INFORMATION* next = _notify.get();
                    while (next != nullptr)
                    {
                        std::wstring fullPath(_path);
                        fullPath.append(L"\\");
                        fullPath.append(std::wstring_view(next->FileName, next->FileNameLength / sizeof(wchar_t)));

                        wprintf(L"ReadChangesAsync %s   ACTION %d \n", fullPath.c_str(), next->Action);

                        WatchObject  object = { fullPath, next->Action };
                        result.push_back(object);

                        if (next->NextEntryOffset)
                        {
                            next = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<char*>(next) + next->NextEntryOffset);
                        }
                        else
                        {
                            next = nullptr;
                        }
                    }
                    _callback(result);
                    break;
                }

                if (token.is_canceled())
                {
                    wprintf(L"watcher cancel received\n");
                    concurrency::cancel_current_task();
                    return;
                }

                Sleep(100);
            }
#if 0
            else if (GetLastError() != WAIT_TIMEOUT)
            {
                throw winrt::hresult_error(HRESULT_FROM_WIN32(GetLastError()));
            }
            else 
#endif
              

           
        }
    }, token);
}

void DirectoryWatcher::Cancel()
{
    wprintf(L"Canceling watcher\n");
    _cancellationTokenSource.cancel();
}

